package com.ifcreate.infowell.client;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ifcreate.infowell.common.util.CustomMap;
import com.ifcreate.infowell.common.util.JsonResult;
import com.ifcreate.infowell.common.util.ObjectUtil;
import com.ifcreate.infowell.entity.Edge;
import com.ifcreate.infowell.entity.InfoWellNode;
import com.ifcreate.infowell.entity.InfoWellRelation;
import com.ifcreate.infowell.repository.InfoWellRepository;
import com.ifcreate.infowell.vo.G6DataEntity;
import jdk.nashorn.internal.runtime.Debug;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringUtils;
import org.neo4j.ogm.model.Result;
import org.neo4j.ogm.response.model.RelationshipModel;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.transaction.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author chenzedeng
 * @Email yustart@foxmail.com
 * @Create 2020-10-26 5:49 下午
 */
@Service
@Log4j2
public class ClientNodeService {

    @Autowired
    private InfoWellRepository repository;

    @Autowired
    private SessionFactory factory;


    //查询子节点和关系
    //match (root)<-[r:InfoWellRelation*..]-(n) where id(root) = 0 return r,n


    /**
     * 查询节点 或者 搜索
     *
     * @param type
     * @param date
     * @param size
     * @param search
     * @return
     */
    public JsonResult getRootNode(Integer type, String date, Integer size, String search) {
        HashSet<Long> NodeVisit = new HashSet<>();
        HashSet<Long> EdgeVisit = new HashSet<>();
        long time = System.currentTimeMillis();
        if (StringUtils.isNotBlank(date)) {
            time = DateUtil.parse(date, "yyyy-MM-dd").getTime();
        }

        List<InfoWellNode> rootNode;

        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();

        //是查询还是搜索
        if (type == null && StringUtils.isNotBlank(search)) {
            rootNode = ListUtil.toList(session.query(InfoWellNode.class, StrUtil.format("match (n)-[*0..]->(root) where root.isNews = true and (n.name=~ '.*{}.*' or n.title=~ '.*{}.*') return root", search, search), CustomMap.create()));
        } else {
            System.out.println("断点");
            StringBuffer sb = new StringBuffer();
            sb.append("match ()-[*0..]->(root) where root.isNews = true  ");
            //sb.append("match ()-[*0..]->(root)");
            if (type != null) {
                sb.append("and root.type = ").append(type).append(" ");
            }
            sb.append("and root.time <= $time  return root order by root.time desc");
            //sb.append("return root order by id(root) desc");
            rootNode = ListUtil.toList(session.query(InfoWellNode.class, sb.toString(), CustomMap.create("time", time))).stream().limit(size).collect(Collectors.toList());
        }

        //查询子节点数据
        try {
            G6DataEntity entity = new G6DataEntity();
            //计算level
            //2-3-5
            double level1 = Math.ceil(rootNode.size() * 0.2);
            double level2 = Math.ceil(rootNode.size() * 0.3);
            double level3 = rootNode.size() - level1 - level2;
            for (int i = 0; i < rootNode.size(); i++) {
                if (NodeVisit.contains(rootNode.get(i).getId())) {
                    continue;
                }
                NodeVisit.add(rootNode.get(i).getId());
                System.out.println(rootNode.get(i).getId());
                rootNode.get(i).setRelations(null);
                Map<String, Object> map = BeanUtil.beanToMap(rootNode.get(i));
                int level = 3;
                if (i <= (level1 - 1)) {
                    level = 1;
                } else if (i <= (level2 - 1)) {
                    level = 2;
                } else if (i <= (level3 - 1)) {
                    level = 3;
                }
                if(!rootNode.get(i).getIsNews()){
                    level = 4;
                }
                map.put("level", level);
                //改变下根目录的数据结构
                if (rootNode.get(i).getTime() != null) {
                    map.put("time", DateUtil.format(new Date(rootNode.get(i).getTime()), "yyyy-MM-dd"));
                }
                if (StringUtils.isNotBlank(rootNode.get(i).getImages())) {
                    map.put("images", JSONUtil.parseArray(rootNode.get(i).getImages()));
                }
                map.put("label", map.remove("name"));

                //子元素
                Result result = session.query("match (root)<-[r:InfoWellRelation*..]-(n) where id(root) = $id return r,n", CustomMap.create("id", rootNode.get(i).getId()));
                Iterator<Map<String, Object>> iterator = result.iterator();
                Set<InfoWellNode> nodes = new HashSet<>();
                Set<Edge> edges = new HashSet<>();

                Set<KeywordData> keyWorlds = new HashSet<>();

                while (iterator.hasNext()) {
                    Map<String, Object> next = iterator.next();
                    ArrayList rList = (ArrayList) next.get("r");
                    for (Object o : rList) {
                        if (o instanceof InfoWellRelation) {
                            InfoWellRelation relation = (InfoWellRelation) o;
                            Edge edge = new Edge();
                            edge.setId(relation.getId());
                            edge.setRelation(relation.getRelation());
                            edge.setSource(relation.getFrom().getId());
                            edge.setTarget(relation.getTo().getId());
                            edges.add(edge);
                        } else {
                            RelationshipModel model = (RelationshipModel) o;
                            Edge edge = new Edge();
                            edge.setId(model.getId());
                            edge.setRelation((String) model.getPropertyList().get(0).getValue());
                            edge.setSource(model.getStartNode());
                            edge.setTarget(model.getEndNode());
                            edges.add(edge);
                        }
                    }
                    InfoWellNode node = (InfoWellNode) next.get("n");
                    if(!NodeVisit.contains(node.getId())) {
                        rootNode.add(node);
                        nodes.add(node);
                        KeywordData keywordData = new KeywordData();
                        keywordData.setKey(node.getName());
                        keywordData.setType(node.getType());
                        keyWorlds.add(keywordData);
                    }
                }


                result = session.query("match (root)-[r:InfoWellRelation*..]->(n) where id(root) = $id return r,n", CustomMap.create("id", rootNode.get(i).getId()));
                iterator = result.iterator();


                while (iterator.hasNext()) {
                    Map<String, Object> next = iterator.next();
                    ArrayList rList = (ArrayList) next.get("r");
                    for (Object o : rList) {
                        if (o instanceof InfoWellRelation) {
                            InfoWellRelation relation = (InfoWellRelation) o;
                            Edge edge = new Edge();
                            edge.setId(relation.getId());
                            edge.setRelation(relation.getRelation());
                            edge.setSource(relation.getFrom().getId());
                            edge.setTarget(relation.getTo().getId());
                            edges.add(edge);
                        } else {
                            RelationshipModel model = (RelationshipModel) o;
                            Edge edge = new Edge();
                            edge.setId(model.getId());
                            edge.setRelation((String) model.getPropertyList().get(0).getValue());
                            edge.setSource(model.getStartNode());
                            edge.setTarget(model.getEndNode());
                            edges.add(edge);
                        }
                    }
                    InfoWellNode node = (InfoWellNode) next.get("n");
                    if(!NodeVisit.contains(node.getId())) {
                        rootNode.add(node);
                        nodes.add(node);
                        KeywordData keywordData = new KeywordData();
                        keywordData.setKey(node.getName());
                        keywordData.setType(node.getType());
                        keyWorlds.add(keywordData);
                    }
                }



                entity.addEdgeAll(edges);
//                entity.addNodeAll(nodes.stream().map(m -> {
//                    m.setRelations(null);
//                    Map<String, Object> toMap = BeanUtil.beanToMap(m);
//                    if (!m.getIsNews()) {
//                        toMap.put("level", 4);
//                    } else {
//                        toMap.put("level", 3);
//                    }
//                    //改变下根目录的数据结构
//                    if (m.getTime() != null) {
//                        toMap.put("time", DateUtil.format(new Date(m.getTime()), "yyyy-MM-dd"));
//                    }
//                    if (StringUtils.isNotBlank(m.getImages())) {
//                        toMap.put("images", JSONUtil.parseArray(m.getImages()));
//                    }
//                    toMap.put("label", toMap.remove("name"));
//                    toMap.put("infoContent", Base64.encode(m.getInfoContent()));
//                    return toMap;
//                }).collect(Collectors.toList()));

                //设置关键字高亮
                //map.put("infoContent", Base64.encode(keywordHighlight(rootNode.get(i).getInfoContent(), keyWorlds)));
                String decodeStr = Base64.decodeStr(rootNode.get(i).getInfoContent());
                String encodeStr = Base64.encode(keywordHighlight(decodeStr, keyWorlds));
                map.put("infoContent", encodeStr);

                entity.addNode(map);
            }
            transaction.commit();


            //去重复
            //entity.resetNodes(entity.getNodes().stream().filter(ObjectUtil.distinctByKey(m -> MapUtil.getLong(m, "id"))).collect(Collectors.toList()));
            return JsonResult.success(entity);
        } catch (Exception e) {
            log.error(e);
        } finally {
            session.clear();
        }
        return JsonResult.actionFailure();
    }

    private String keywordHighlight(String html, Set<KeywordData> keywordData) {
        if (StringUtils.isBlank(html)) {
            return "";
        }
        String template = "<span {}>{}</span>";
        for (KeywordData data : keywordData.stream().sorted((o1, o2) -> Integer.compare(o2.getKey().length(), o2.getKey().length())).collect(Collectors.toList())) {
            html = html.replaceAll(data.getKey(), StrUtil.format(template, data.getStyle(), data.getKey()));
        }
        return html;
    }


    /**
     * 获取时间的间隔
     *
     * @return
     */
    public JsonResult getTimeInterval() {
        Session session = factory.openSession();
        try {
            Long minTime = session.queryForObject(Long.class, "match (n) return n.time order by n.time asc limit 1", CustomMap.create());
            Long maxTime = session.queryForObject(Long.class, "match (n) return n.time order by n.time desc limit 1", CustomMap.create());

            CustomMap map = CustomMap.create();
            map.put("startTime", minTime);
            map.put("endTime", maxTime);
            map.put("startDate", DateUtil.format(new Date(minTime), "yyyy-MM-dd"));
            map.put("endDate", DateUtil.format(new Date(maxTime), "yyyy-MM-dd"));
            return JsonResult.success(map);
        } catch (Exception e) {
            log.error(e);
        } finally {
            session.clear();
        }
        return JsonResult.actionFailure();
    }


    /**
     * 自增点击次数
     *
     * @param id
     * @return
     */
    public JsonResult addClickCount(Long id) {
        Optional<InfoWellNode> optional = repository.findById(id);
        if (!optional.isPresent()) {
            return JsonResult.error("节点不存在");
        }
        InfoWellNode node = optional.get();
        node.setClickCount(node.getClickCount() + 1);
        repository.save(node);
        return JsonResult.success(node);
    }


    /**
     * 返回热门
     *
     * @param size
     * @return
     */
    public JsonResult getHostNameList(Integer size) {
        Session session = factory.openSession();
        try {
            Iterable<String> query = session.query(String.class, "match (n)  where n.clickCount > 0 return  n.name order by n.clickCount desc limit $size", CustomMap.create("size", size));
            return JsonResult.success(ListUtil.toList(query.iterator()));
        } catch (Exception e) {
            log.error(e);
        } finally {
            session.clear();
        }
        return JsonResult.actionFailure();
    }

    @Data
    private class KeywordData {
        private String key;
        private Integer type;


        /**
         * 获取样式
         *
         * @return
         */
        public String getStyle() {
            StringBuffer sb = new StringBuffer();
            sb.append("style='");
            switch (this.type) {
                case 1:
                    sb.append("background: rgba(255, 157, 77, 0.2);border: 1px solid #FF9D4D;border-radius: 1px;");
                    break;
                case 2:
                    sb.append("background: rgba(109, 200, 236, 0.2);border: 1px solid #6DC8EC;border-radius: 1px;");
                    break;
                case 3:
                    sb.append("background: rgba(100, 151, 255, 0.2);border: 1px solid #6497FF;border-radius: 1px;");
                    break;
                case 4:
                    sb.append("background: rgba(222, 222, 222, 0.2);border: 1px solid #DEDEDE;border-radius: 1px;");
                    break;
            }
            sb.append("'");
            return sb.toString();
        }
    }

}
